﻿using Dotnet.Utils.Utility.ObjectExtensions.Str;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Dotnet.Utils.Utility.ObjectExtensions.Date
{
    /// <summary>
    /// 定义周期类型
    /// </summary>
    public class 周期类型
    {
        /// <summary>年</summary>
        public const char 年 = 'A';
        /// <summary>季</summary>
        public const char 季 = 'B';
        /// <summary>月</summary>
        public const char 月 = 'C';
        /// <summary>周</summary>
        public const char 周 = 'D';

        /// <summary>
        /// 获取周期类型名称
        /// </summary>
        /// <param name="c"><see cref="char"/>周期类型名称</param>
        /// <returns><see cref="string"/>返回周期类型</returns>
        public static string GetTypeName(char c)
        {
            string sResult;
            switch (c)
            {
                case 年:
                    sResult = "年";
                    break;
                case 季:
                    sResult = "季";
                    break;
                case 月:
                    sResult = "月";
                    break;
                case 周:
                    sResult = "周";
                    break;
                default:
                    sResult = "";
                    break;
            }
            return sResult;
        }
    }
    /// <summary>
    /// 系统时间段工具
    /// </summary>
    /// <remarks>
    /// DateTimeUtil 获取系统所有的时间类别。
    /// 系统中所有的时间范围需要从此获取，系统中的时间范围规则是大于等于最小时间，小于最大时间
    /// 如：表示4月份的时间是 >=2004.4.1 小于2004.5.1 所用到的SQL语句等都必须遵循此方法。
    /// </remarks>
    public class DateSpanUtil
    {
        #region 修改前代码 
        //		public static DateTime YearStartDate(string sSpanTypeCode,int year,int yearSpan)
        //		{
        //			//生成年编码
        //			year=year+yearSpan;
        //			string sYear=year.ToString()+"01A";
        //			//没有,在时间段基本表里找
        //			InsDatespanBaseEntity spanBase=InsDatespanBaseUtil.GetInsDatespanBase(sSpanTypeCode);
        //			if(spanBase==null)
        //				throw new ArgumentException("没有当前的周期类型："+sSpanTypeCode);
        //			//int iTypeId = spanBase.Id;
        //			//在时间段明细表里查找是否有记录
        //			bool bYearStartDate= InsDatespanDetailUtil.IsInsDatespanDetailExist(sSpanTypeCode,sYear);
        //			if(bYearStartDate)
        //			{
        //				//有,返回起始时间
        //				InsDatespanDetailEntity spandetail=InsDatespanDetailUtil.GetInsDatespanDetail(sSpanTypeCode,sYear);
        //				return spandetail.Start_date;
        //			}
        //			else
        //			{
        //				//生成起始时间
        //				DateTime startDate = new DateTime(year,spanBase.Start_date.Month,spanBase.Start_date.Day);
        //				if(spanBase.Start_last==((int)是否状态.是).ToString())
        //				{
        //					return startDate.AddYears(-1);
        //				}
        //				else
        //				{
        //					return startDate;
        //				}
        //			}
        //		}
        #endregion



        /// <summary>
        /// 获取输入日期的周索引
        /// </summary>
        /// <param name="curDate"><see cref="System.DateTime"/>，日期</param>
        /// <returns><see cref="int"/>，返回周索引</returns>
        /// <remarks>
        /// 返回某日是一年的第几周,一年的第一周为一年中第一个星期日开始的那周
        /// </remarks>
        /// <example>
        /// <code>
        /// DateTime curTime = DateTime.Now;
        /// int iIndex = DateSpanUtil.GetWeekIndexOfYear(curTime);
        /// </code>
        /// </example>
        public static int GetWeekIndexOfYear(DateTime curDate)
        {
            DateTime firstDate = new DateTime(curDate.Year, 1, 1);
            int firstWeekDay = (int)firstDate.DayOfWeek;
            if (firstWeekDay > 0)
                firstDate = new DateTime(curDate.Year, 1, 8 - firstWeekDay);
            TimeSpan span = curDate - firstDate;
            return span.Days / 7 + 1;
        }

        /// <summary>
        /// 返回指定日期所在的季度。
        /// </summary>
        /// <param name="curDate">日期。</param>
        /// <returns>指定日期所在的季度。</returns>
        public static int GetSeasonIndexOfYear(DateTime curDate)
        {
            return (int)Math.Ceiling(curDate.Month / 3.0);
        }

        /// <summary>
        /// 获取周期编码信息
        /// </summary>
        /// <param name="curSpanCode"><see cref="string"/>，当前周期编码</param>
        /// <param name="iDeltaSpan"><see cref="int"/>，周期间隔</param>
        /// <returns><see cref="string"/>，返回周期编码</returns>
        /// <example>
        /// 如获取200501A的上周期编码 200501A -1，返回 200401A.
        /// <code>
        /// System.Console.Write(DateSpanUtil.GetSpanCode("200501A",-1));
        /// </code>
        /// <B>输出：</B>200401A
        /// </example>
        public static string GetSpanCode(string curSpanCode, int iDeltaSpan)
        {
            if (curSpanCode.Length != 7)
            {
                throw new ArgumentException("周期编码应该为七位！");
            }
            char sSpanType = curSpanCode[6];
            int iYear = int.Parse(curSpanCode.Substring(0, 4));
            int iSpan = int.Parse(curSpanCode.Substring(4, 2));
            DateTime curDate;
            switch (sSpanType)
            {
                case 周期类型.周:
                    curDate = new DateTime(iYear, 1, 1);
                    DateTime temp;
                    GetWeekDateSpan(curSpanCode, out curDate, out temp);
                    curDate = curDate.AddDays(7 * iDeltaSpan);
                    //curDate = curDate.AddDays(7-(int)curDate.DayOfWeek+(iSpan+iDeltaSpan)*7);
                    int iIndex = GetWeekIndexOfYear(curDate);
                    return curDate.Year + iIndex.ToString("00") + "D";
                case 周期类型.季:
                    //*************************
                    //修改人：胡峰
                    //修改时间：2006/5/29
                    //修改内容：此处获得季度编码的方式有有问题，进行修改。
                    //curDate = new DateTime(iYear,iSpan*3,1);
                    //curDate = curDate.AddMonths(3*iDeltaSpan);
                    //return curDate.Year+"0"+(curDate.Month/3+curDate.Month%3!=0?1:0)+sSpanType;
                    bool bIsNegative = iDeltaSpan < 0;
                    while (Math.Abs(iDeltaSpan) != 0)
                    {
                        iSpan += bIsNegative ? -1 : 1;
                        iDeltaSpan += bIsNegative ? 1 : -1;
                        if (iSpan < 1)
                        {
                            iSpan = 4;
                            iYear -= 1;
                        }
                        else if (iSpan > 4)
                        {
                            iSpan = 1;
                            iYear += 1;
                        }
                    }
                    return iYear.ToString() + (iSpan).ToString().PadLeft(2, '0') + sSpanType;
                //*************************
                case 周期类型.年:
                    curDate = new DateTime(iYear, 1, 1);
                    curDate = curDate.AddYears(iDeltaSpan);
                    return curDate.Year + "01A";
                case 周期类型.月:
                    curDate = new DateTime(iYear, iSpan, 1);
                    curDate = curDate.AddMonths(iDeltaSpan);
                    return curDate.ToString("yyyyMM") + sSpanType;
                default:
                    throw new ArgumentException("错误的参数！");
            }

        }

        /// <summary>
        /// 获取本周开始的日期和结束日期
        /// </summary>
        /// <param name="curDate"><see cref="System.DateTime"/>，输入日期</param>
        /// <param name="startDate"><see cref="System.DateTime"/>，开始日期</param>
        /// <param name="endDate"><see cref="System.DateTime"/>，结束日期</param>
        /// <remarks>根据当前日期获取本周开始的日期和结束日期（下周开始日期）</remarks>
        /// <example>
        /// <code>
        /// DateTime dt = DateTime.Now;
        /// DateTime startDate,endDate;
        /// DateSpanUtil.GetWeekDateSpan(dt,out startDate,out endDate);
        /// </code>
        /// </example>
        public static void GetWeekDateSpan(DateTime curDate, out DateTime startDate, out DateTime endDate)
        {
            curDate = curDate.Date;
            int week = (int)curDate.DayOfWeek;
            startDate = curDate.AddDays(7 - week);
            endDate = startDate.AddDays(7);
        }
        /// <summary>
        /// 得到某个值描述的周时间段范围
        /// </summary>
        /// <param name="sSpan"><see cref="string"/>，输入时间段</param>
        /// <param name="startTime"><see cref="System.DateTime"/>，开始日期</param>
        /// <param name="endTime"><see cref="System.DateTime"/>，结束日期</param>
        /// <example>
        /// <code>
        /// DateTime startDate,endDate;
        /// string sMonth = "200512C";
        /// DateSpanUtil.GetWeekDateSpan(sMonth,out startDate,out endDate);
        /// </code>
        /// </example>
        public static void GetWeekDateSpan(string sSpan, out DateTime startTime, out DateTime endTime)
        {
            startTime = DateTime.MinValue;
            endTime = DateTime.MinValue;
            if (sSpan.Length != 7)
                return;
            int iYear = int.Parse(sSpan.Substring(0, 4));
            int iWeeks = int.Parse(sSpan.Substring(4, 2));

            DateTime yearStart = new DateTime(iYear, 1, 1);
            int iWeekStart = (int)yearStart.DayOfWeek;
            if (iWeekStart != 0)
            {
                yearStart = yearStart.AddDays(7 - iWeekStart);
            }
            startTime = yearStart.AddDays((iWeeks - 1) * 7);
            endTime = startTime.AddDays(6);
        }
        /// <summary>
        /// 根据某一日期获得月的编码
        /// </summary>
        /// <param name="dt"><see cref="System.DateTime"/>，日期</param>
        /// <returns><see cref="string"/>，返回月编码</returns>
        /// <example>
        /// <code>
        /// string sSpanCode = DateSpanUtil.GetMonthCode(DateTime.Now);
        /// </code>
        /// </example>
        public static string GetMonthCode(DateTime dt)
        {
            int iYear = dt.Year;
            string sMonth = dt.Month.ToString("0#");
            return iYear.ToString() + sMonth + "C";
        }
        /// <summary>
        ///  根据月的编码获得这某年某月的第一天
        /// </summary>
        /// <param name="sMonthCode"><see cref="string"/>，月的编码</param>
        /// <returns><see cref="System.DateTime"/>，返回自然月第1天</returns>
        /// <example>
        /// <code>
        /// string sSpanCode = "200512C";
        /// DateTime dt = DateSpanUtil.DecodeMonthCode(sSpanCode);
        /// System.Consolse.Write(dt.ToShortDateString());
        /// </code>
        /// <B>输出：</B>2005-12-1
        /// </example>
        public static DateTime DecodeMonthCode(string sMonthCode)
        {
            int iYear = int.Parse(sMonthCode.Substring(0, 4));
            int iMonth = int.Parse(sMonthCode.Substring(4, 2));
            return new DateTime(iYear, iMonth, 1);
        }
        /// <summary>
        /// 获取由年月表示的形如200401字符串
        /// </summary>
        /// <param name="year"><see cref="int"/>，年</param>
        /// <param name="month"><see cref="int"/>，月</param>
        /// <returns><see cref="string"/>，返回年月输出字符串</returns>
        /// <example>
        /// <code>
        /// DateTime dt = DateTime.Now;
        /// string sSpanCode = DateSpanUtil.GetYearMonth(DateTime.Year,DateTime.Month);
        /// </code>
        /// </example>
        public static string GetYearMonth(int year, int month)
        {
            string result = year.ToString();
            if (month < 10)
                result += "0";
            result += month.ToString();
            return result;
        }

        /// <summary>
        /// 获取指定年份代表的周期的编码
        /// </summary>
        /// <param name="iYear">年份</param>
        /// <returns>指定年份代表的周期的编码</returns>
        public static string GetYearSpanCode(int iYear)
        {
            return iYear.ToString("0000") + "01" + 周期类型.年;
        }
        /// <summary>
        /// 比较周期编码是否在指定的范围内
        /// </summary>
        /// <param name="spancode"></param>
        /// <param name="minSpancode"></param>
        /// <param name="maxSpancode"></param>
        /// <returns></returns>
        public static bool CompareSpanCode(string spancode, string minSpancode, string maxSpancode)
        {
            int min = StrHelper.ParseInt(minSpancode.Substring(0, 6), 0);
            int max = StrHelper.ParseInt(maxSpancode.Substring(0, 6), 0);
            int span = StrHelper.ParseInt(spancode.Substring(0, 6), 0);
            if (span <= max && span >= min)
                return true;
            else
                return false;
        }
        /// <summary>
        /// 获取附近的周期编码
        /// </summary>
        /// <param name="sSpanCode"></param>
        /// <param name="deltaSpan"></param>
        /// <returns></returns>
        public static string GetNearbySpanCode(string sSpanCode, int deltaSpan)
        {
            int year = Int32.Parse(sSpanCode.Substring(0, 4));
            int span = Int32.Parse(sSpanCode.Substring(4, 2)) - 1;
            char sType = sSpanCode[6];
            int iSize;
            switch (sType)
            {
                case 'A': iSize = year + deltaSpan; year = year; break;
                case 'B': iSize = year * 4 + span + deltaSpan; year = iSize / 4; span = iSize % 4; break;
                case 'C': iSize = year * 12 + span + deltaSpan; year = iSize / 12; span = iSize % 12; break;
                case 'D': iSize = year * 57 + span + deltaSpan; year = iSize / 57; span = iSize % 57; break;
            }
            span += 1;
            return year.ToString("0000") + span.ToString("00") + sType;
        }
        /// <summary>
        /// 获取指定年份与季度代表的周期的编码
        /// </summary>
        /// <param name="iYear">年份</param>
        /// <param name="iSeason">季度</param>
        /// <returns>指定年份与季度代表的周期的编码</returns>
        public static string GetSeasonSpanCode(int iYear, int iSeason)
        {
            return iYear.ToString("0000") + iSeason.ToString("00") + 周期类型.季;
        }

        /// <summary>
        /// 获取指定年份与月份代表的周期的编码
        /// </summary>
        /// <param name="iYear">年份</param>
        /// <param name="iMonth">月份</param>
        /// <returns>指定年份与月份代表的周期的编码</returns>
        public static string GetMonthSpanCode(int iYear, int iMonth)
        {
            return iYear.ToString("0000") + iMonth.ToString("00") + 周期类型.月;
        }

        /// <summary>
        /// 获取指定年份的某一周代表的周期的编码
        /// </summary>
        /// <param name="iYear">年份</param>
        /// <param name="iWeek">指示指定年份的第几周</param>
        /// <returns>指定年份的某一周代表的周期的编码</returns>
        public static string GetWeekSpanCode(int iYear, int iWeek)
        {
            return iYear.ToString("0000") + iWeek.ToString("00") + 周期类型.周;
        }

        /// <summary>
        /// 传入一个日期与周期类型，返回对应周期的编码
        /// </summary>
        /// <param name="dtDate">日期</param>
        /// <param name="cType"><see cref="周期类型"/></param>
        /// <returns>返回对应周期的编码</returns>
        /// <remarks>
        /// 如果传入周周期类型，则返回空字符串
        /// </remarks>
        /// <example>
        /// <code>
        /// DateTime dtDate = new DateTime(2008, 12, 1);
        /// string sYearSpanCode = DateSpanUtil.GetSpanCode(DateTime.Now, 周期类型.年);
        /// //sYearSpanCode = "200801A";
        /// string sSeasonSpanCode = DateSpanUtil.GetSpanCode(DateTime.Now, 周期类型.季);
        /// //sSeasonSpanCode = "200804B";
        /// string sMonthSpanCode = DateSpanUtil.GetSpanCode(DateTime.Now, 周期类型.月);
        /// //sMonthSpanCode = "200812C";
        /// </code>
        /// </example>
        public static string GetSpanCode(DateTime dtDate, char cType)
        {
            string sSpanCode = string.Empty;
            switch (cType)
            {
                case 周期类型.年:
                    sSpanCode = GetYearSpanCode(dtDate.Year);
                    break;
                case 周期类型.季:
                    int iSeason = ((int)(dtDate.Month - 1) / 3) + 1;
                    sSpanCode = GetSeasonSpanCode(dtDate.Year, iSeason);
                    break;
                case 周期类型.月:
                    sSpanCode = GetMonthSpanCode(dtDate.Year, dtDate.Month);
                    break;
                case 周期类型.周:
                    sSpanCode = GetWeekSpanCode(dtDate.Year, GetWeekIndexOfYear(dtDate));
                    break;
                default:
                    break;
            }
            return sSpanCode;
        }

        public static string GetSpanName(string sSpanCode)
        {
            if (string.IsNullOrEmpty(sSpanCode))
                return sSpanCode;
            string sName = sSpanCode.Substring(0, 4) + "年";
            string sChar = sSpanCode.Substring(sSpanCode.Length - 1);
            if (sChar != "A")
                sName += sSpanCode.Substring(4, 2);
            if (sChar == "B")
                sName += "季";
            else if (sChar == "C")
                sName += "月";
            else if (sChar == "D")
                sName += "周";
            return sName;
        }
    }
}